#include <fcntl.h>
#include <linux/gpio.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <unistd.h>

#include "platforms/gpio.h"
#include "retval.h"

#define GPIO_NODE_NAME_LEN     256
#define GPIO_NUM_MAX           256
#define GPIO_EXPORT_PATH       "/sys/class/gpio/export"
#define GPIO_DIRECTION_PATH    "/sys/class/gpio/gpio%d/direction"
#define GPIO_EXPORT_VALUE_PATH "/sys/class/gpio/gpio%d/value"

static int fds[GPIO_NUM_MAX] = { 0 };

int gpio_dir(uint32_t num, gpio_dir_t dir)
{
	int fd;
	char direction[8];
	char export_path[GPIO_NODE_NAME_LEN];
	char direction_path[GPIO_NODE_NAME_LEN];

	/* 参数有效性校验 */
	if (num >= GPIO_NUM_MAX) {
		printf("inval: num\n");
		return -EINVAL;
	}

	switch (dir) {
	case GPIO_E_INPUT:
		snprintf(direction, sizeof(direction), "%s", "in");
		break;
	case GPIO_E_OUTPUT:
		snprintf(direction, sizeof(direction), "%s", "out");
		break;
	default:
		printf("inval: dir\n");
		return -EINVAL;
	}

	/* 导出 GPIO 节点，使其可被其他程序使用 */
	snprintf(export_path, sizeof(export_path), GPIO_EXPORT_PATH);
	fd = open(export_path, O_WRONLY);
	if (fd < 0) {
		printf("err: open(%s, O_WRONLY), ret = %d\n", export_path, fd);
		return 1;
	}
	dprintf(fd, "%d", num);
	close(fd);

	/* 设置 GPIO 方向 */
	snprintf(direction_path, sizeof(direction_path), GPIO_DIRECTION_PATH, num);
	fd = open(direction_path, O_WRONLY);
	if (fd < 0) {
		printf("err: open(%s, O_WRONLY), ret = %d\n", direction_path, fd);
		return 1;
	}
	dprintf(fd, direction);
	close(fd);

	return 0;
}

int gpio_set(uint32_t num, gpio_val_t val)
{
	int fd = 0;
	char value[8];
	char path[GPIO_NODE_NAME_LEN];

	/* 参数有效性校验 */
	if (num >= GPIO_NUM_MAX) {
		printf("inval: num\n");
		return -EINVAL;
	}

	switch (val) {
	case GPIO_E_LOW:
		snprintf(value, sizeof(value), "%s", "0");
		break;
	case GPIO_E_HIGH:
		snprintf(value, sizeof(value), "%s", "1");
		break;
	default:
		printf("inval: val\n");
		return -EINVAL;
	}

	if (0 == fds[num]) {
		snprintf(path, sizeof(path), GPIO_EXPORT_VALUE_PATH, num);
		fd = open(path, O_WRONLY);
		if (fd < 0) {
			perror("无法打开 GPIO 值节点");
			return 1;
		}
		dprintf(fd, value);
		fds[num] = fd;
	} else {
		dprintf(fds[num], value);
	}

	return 0;
}

int gpio_get(int num)
{
	return 0;
}